package sketch.specs;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.internal.core.dom.NaiveASTFlattener;

import sketch.util.Checker;

public class UsedVariableVisitor extends NaiveASTFlattener {
	
	protected final Collection<SimpleName> variablesToCheck;
	
	private final Set<SimpleName> usedVariables;
	
	//keep the connection between defined var and used vars
	private Map<SimpleName, List<SimpleName>> dumap = new LinkedHashMap<SimpleName, List<SimpleName>>();
	
	public UsedVariableVisitor(Collection<SimpleName> variables) {
		Checker.checkNull(variables, "The variable list could not be null.");
		this.variablesToCheck = variables;
		usedVariables = new LinkedHashSet<SimpleName>();
	}
	
	public Collection<SimpleName> getUsedVariables() {
		return this.usedVariables;
	}
	
	Map<SimpleName, List<SimpleName>> getDUMap() {
		return this.dumap;
	}

	//the implementation may not be fully correct! SimpleName could appear at many places.
	//here we only avoid to replace SimpleName which is served as method-name
	//TODO
	@Override
	public boolean visit(SimpleName name) {
		//check each variable
		SimpleName defVar = null;
		for(SimpleName var : this.variablesToCheck) {
			
			if(name.getStartPosition() != var.getStartPosition()
					&& name.toString().equals(var.toString())) {
				//System.out.println("*** name's parent: " + name.getParent().getClass());
				if(!isFalsePositive(name)) {
					defVar = var;
				    Checker.checkTrue(var.getAST() == name.getAST(), "Can not compared if two asts are different");
				    break;
				}
			}
		}
		
		if(defVar != null) {
			this.usedVariables.add(defVar);
			this.updateDumap(defVar, name);
		}
		
		return super.visit(name);
	}
	
	private void updateDumap(SimpleName def, SimpleName use) {
		if(!this.dumap.containsKey(def)) {
			this.dumap.put(def, new LinkedList<SimpleName>());
		}
		dumap.get(def).add(use);
	}
	
	//to see is the variable appearing in an incorrect place?
	//such as the same as method name
	//e.g. String name = Values.choose("a", "b", "c");
	//     name(name)
	//The first name token is simply the method identity, whiel the
	//second one needs to be replaced, the results should be: name("a"), name("b"), name("c")
	public static boolean isFalsePositive(SimpleName name) {
		if(name.getParent() instanceof MethodInvocation) {
			if(((MethodInvocation)name.getParent()).getName() == name) {
				return true;
			} else {
				return false;
			}
		} else {
		    return false;
		}
	}
}
